/*
 * Copyright (C) 2012 POAS.VSTU
 */
package model;

import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.event.EventListenerList;

/** Класс игровой модели. */
public class GameModel {

    /// Список слушателей событий модели игры
    protected EventListenerList listenerList = new EventListenerList();

    /// Текущий статус
    private ModelStatus mCurrentStatus = ModelStatus.UNDEFINED;

    /// Колода карт
    private CardStack mStack = null;

    /// Отбой
    private CardPileRecycle mRecylce = null;

    /// Кон
    private CardPileKitty mKitty = null;

    /// Лишняя карта, убираемая в начале игры
    private CardPile mExtraCards = null;

    /// Игроки
    private CircularBuffer<Player> mPlayers;

    /// Служебный объект синхронизации
    private static final Object sLock = new Object();

    /// Флаг готовности объекта синхронизации
    private static boolean sLockIsReady = false;

    /**
     * Конструктор игровой модели.
     */
    public GameModel() {
        mStack = new CardStack();
        mRecylce = new CardPileRecycle();
        mKitty = new CardPileKitty();
        mExtraCards = new CardPile();
        mPlayers = new CircularBuffer<Player>();
        mPlayers.add(new Gamer("Игрок"));
        mPlayers.add(new ComputerPlayer("Компьютер 1"));
        mPlayers.add(new ComputerPlayer("Компьютер 2"));
        mPlayers.add(new ComputerPlayer("Компьютер 3"));

        reset();
    }

    /**
     * Получить массив всех игроков.
     *
     * Первым в массиве идет игрок-человек.
     * @return массив игроков
     */
    public CircularBuffer<Player> getAllPlayers() {
        return mPlayers;
    }

    /** Текущий статус игры. */
    public ModelStatus getStatus() {
        return mCurrentStatus;
    }

    /** Установить статус. */
    private void setStatus(ModelStatus status) {
        mCurrentStatus = status;
    }

    /** Колода карт. */
    public CardStack getStack() {
        return mStack;
    }

    /** Кон. */
    public CardPileKitty getKitty() {
        return mKitty;
    }

    /** Отбой. */
    public CardPileRecycle getRecycle() {
        return mRecylce;
    }

    /** Лишняя карта. */
    public CardPile getExtraCards() {
        return mExtraCards;
    }

    /**
     * Начать игру заново.
     */
    public void reset() {
        System.out.println("Я хочу сыграть с тобой");
        // Собрать карты в колоду

        mStack.shuffle();
        moveExtraCardFromStack();
        dealCards();
        for (Object o: mPlayers.toArray()) {
            Player p = (Player) o;
            System.out.println(p.getName() + ':' + p.getCards().size() + " карт");
            System.out.println();
            for (Card c: p.getCards()) {
                System.out.println(c);
            }
        }
        moveTetradsFromPlayersToRecycle();

        // Ожидание хода игрока
        setStatus(ModelStatus.WAITING_FOR_PLAYER_ACTION);
    }

    /**
     * Собрать все карты в колоду на кону.
     *
     * @return CardPileKitty колода карт на кону
     */
    private CardPileKitty moveAllCardsToKittyPile() {
        return null;
    }

    /** Переместить первую карту из колоды */
    private void moveExtraCardFromStack() {
        // Установить статус
        setStatus(ModelStatus.DROPPING_FIRST_CARD);

        // Сбросить верхнюю карту из колоды
        Card first = mStack.pop();
        // Сигнал перемещения карт

        // Добавить лишнюю карту
        mExtraCards.push(first);
    }
    /**
     * Раздать карты игрокам.
     */
    private void dealCards() {
        setStatus(ModelStatus.DEALING_CARDS);

        Iterator<Player> playerIterator = mPlayers.iterator();
        Card card = null;
        // Раздать карты игрокам
        while ((card = mStack.pop()) != null) {
            //System.out.println(card);
            Player player = playerIterator.next();
            // Испустить сигнал перемещения карт
            player.getCards().push(card);
        }

    }

    /**
     * Сбросить четверки карт всех игроков в отбой.
     *
     * @return int количество сброшенных в отбой карт
     */
    private int moveTetradsFromPlayersToRecycle() {
        Object[] players = mPlayers.toArray();
        for (int i = 0; i < players.length; i++) {
            Player player = (Player) players[i];
            // Сбросить все четверки карт игрока

            //Card[] tetrad;
            //while ((tetrad = player.getCards().dropTetrad()) != null) {
                // Установить статус
            //    setStatus(ModelStatus.DROPPING_TETRAD);
                // Сигнал, что карты перемещаются

                // Переместить карты в отбой
                //mRecylce.push(tetrad);
                // Сигнал, что карты перемещены
                // Вернуть статус
            //    setStatus(ModelStatus.UNDEFINED);
            //}

        }
        return 0;
    }

    /** Разбудить поток модели. */
    public void wakeUp() {
        synchronized(sLock) {
            System.out.println("Поток разбужен.");
            sLockIsReady = true;
            sLock.notifyAll();
        }
    }

    /** Приостановить выполнение потока модели. */
    public void sleep() {
        synchronized(sLock) {
            while(!sLockIsReady) {
                System.out.println("Поток приостановлен.");
                try {
                    sLock.wait();
                } catch (InterruptedException ex) {
                    Logger.getLogger(GameModel.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }

    /**
     * Испустить событие "карты перемещаются".
     *
     * @param event испускаемое событие
     */
    private void fireCardsMovedEvent(GameEvent event) {
        Object[] listeners = listenerList.getListenerList();
        // Оповестить слушателей в порядке, обратном добавлению
        for (int i = listeners.length - 1; i >= 0; i--) {
            ((GameEventListener)listeners[i]).cardsMoved(event);
        }
    }

    /**
     * Испустить событие "карты извлечены".
     *
     * @param event испускаемое событие
     */
    private void fireCardsExtractedEvent(GameEvent event) {
        Object[] listeners = listenerList.getListenerList();
        // Оповестить слушателей в порядке, обратном добавлению
        for (int i = listeners.length - 1; i >= 0; i--) {
            ((GameEventListener)listeners[i]).cardsExtracted(event);
        }
    }

    /**
     * Испустить событие "карты добавлены".
     *
     * @param event испускаемое событие
     */
    private void fireCardsAddedEvent(GameEvent event) {
        Object[] listeners = listenerList.getListenerList();
        // Оповестить слушателей в порядке, обратном добавлению
        for (int i = listeners.length - 1; i >= 0; i--) {
            ((GameEventListener)listeners[i]).cardsAdded(event);
        }
    }

    /**
     * Добавить слушателя событий.
     *
     * @param l добавляемый слушатель событий
     */
    public void addGameEventListener(GameEventListener l) {
        listenerList.add(GameEventListener.class, l);
    }

    /**
     * Удаление слушателя событий.
     *
     * @param l слушатель, которого нужно забыть
     */
    public void removeGameEventListener(GameEventListener l) {
        listenerList.remove(GameEventListener.class, l);
    }

}
